package com.csaba.connector.bha;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import com.csaba.connector.ServiceException;
import com.csaba.connector.bha.model.BHABank;
import com.csaba.connector.bha.model.BHACurrentAccount;
import com.csaba.connector.bha.model.BHADepositAccount;
import com.csaba.connector.bha.model.BHALoanAccount;
import com.csaba.connector.bha.model.BHASession;
import com.csaba.connector.model.Amount;
import com.csaba.connector.model.HistoryItem;
import com.csaba.connector.model.Session;
import com.csaba.connector.service.AbstractAccountHistoryService;
import com.csaba.util.LoggerHelper;
import com.ind.simplenode.SimpleNode;

public class BHAAccountHistoryService extends AbstractAccountHistoryService
{
	private final static Logger logger = LoggerHelper.getDefaultLogger();

	public final static String SP_FUNCTION_ACCOUNTS = "AccHistory.getFunctionAccounts";
	private final static String[] DEPOSIT_CODES = new String[] { "PC", "I", "I1", "PM" };
	private final static int[] DEPOSIT_CODE_SINGS = new int[] { +1, +1, -1, -1 };

	private void currentHistory( final Session session, final BHACurrentAccount account ) throws ServiceException
	{
		// first get function accounts
		if ( !session.isRemotePropertySet(SP_FUNCTION_ACCOUNTS) )
		{
			/* Service name: getFunctionAccounts
			 * Parameters:
			 * enabledActions.list(boolean) [true]
			 */
			final SimpleNode funcAccountRequest = new SimpleNode();

			funcAccountRequest.setBooleanPath("enabledActions.list", true);
			final SimpleNode funcAccountReply = ( (BHASession) session ).sendRequest("AccHistory",
					"getFunctionAccounts", funcAccountRequest);

			session.setRemoteProperty(SP_FUNCTION_ACCOUNTS, funcAccountReply);
			/* function account reply
			.accountsWithActions[0].enabledActions.list = 'true'
			.accountsWithActions[0].account.suffix = 'xxx'
			.accountsWithActions[0].account.giro1 = 'xxxxxxxx'
			.accountsWithActions[0].account.customerId = 'xxxxxx'
			.accountsWithActions[0].account.currency = 'HUF'
			.accountsWithActions[0].account.branch = 'xxxx'
			.accountsWithActions[0].account.giro3 = 'xxxxxxxx'
			.accountsWithActions[0].account.commissionType.code = 'CSA'
			.accountsWithActions[0].account.accountName = 'aaaa'
			.accountsWithActions[0].account.giro2 = 'xxxxxxxx'
			.accounts[0].suffix = 'xxx'
			.accounts[0].giro1 = 'xxxxxxxx'
			.accounts[0].customerId = 'xxxxxx'
			.accounts[0].currency = 'HUF'
			.accounts[0].branch = 'xxxxx'
			.accounts[0].giro3 = 'xxxxxxxx'
			.accounts[0].commissionType.code = 'CSA'
			.accounts[0].accountName = 'aaaaaa'
			.accounts[0].giro2 = 'xxxxxxxx'
			 */
		}
		//verify account enablement based on the function accounts.
		final SimpleNode[] accounts = ( (SimpleNode) session.getRemoteProperty(SP_FUNCTION_ACCOUNTS) )
				.getNodeArray("accounts");
		boolean found = false;
		for ( int i = 0; i < accounts.length; i++ )
		{
			if ( account.equalsNode(accounts[i]) )
			{
				found = true;
				break;
			}
		}
		if ( !found )
		{
			logger.info("Account history is not available for account: " + account);
			return;
		}

		// then get partner list
		if ( !session.isRemotePropertySet("getPartnersAndAccounts") )
		{
			/* Service name: getPartnersAndAccounts
			 * Parameters:
			 * own (String) [N]
			 */
			final SimpleNode partnerRequest = new SimpleNode();

			partnerRequest.set("own", "N");
			final SimpleNode partnerReply = ( (BHASession) session ).sendRequest("AccHistory",
					"getPartnersAndAccounts", partnerRequest);

			session.setRemoteProperty("getPartnersAndAccounts", partnerReply);
			/* getPartnersAndAccounts reply
			partnerAccounts[0].partnerId = 'xxx'
			partnerAccounts[0].status = '0'
			partnerAccounts[0].part3 = 'xxxxxxxx'
			partnerAccounts[0].templateName = ''
			partnerAccounts[0].part2 = 'xxxxxxxx'
			partnerAccounts[0].titleId = '000'
			partnerAccounts[0].narrative = 'aaaaaaaaaaaaa'
			partnerAccounts[0].part1 = 'xxxxxxxxx'
			partnerAccounts[0].partnerAccountId = 'xxxxxxxx'
			...
			partners[0].partnerId = 'xxxxxxxxx'
			partners[0].customerId = 'xxxxxx'
			partners[0].name = 'aaaaaaaaaaaaaaaa'
			partners[0].countryId = 'HU'
			partners[0].own = 'N'
			...
			 */
		}

		//then get account history
		/* Service name: getAccountHistory
		 * Parameters:
		 * branch, customerId, suffix (accnum)
		 * fromDate (Date)
		 * toDate (Date)
		 * [partnername]
		 * accountGiro1, accountGiro2, accountGiro3 [partneraccountnumber]
		 * minAmount (Double)
		 * maxAmount (Double)
		 * creditDebitBoth [C/D/B]
		 */

		final SimpleNode request = new SimpleNode();

		request.set("branch", account.getBranch());

		request.set("customerId", account.getCustomerId());
		request.set("suffix", account.getSuffix());
		request.setDate("fromDate", from);
		request.setDate("toDate", to);
		request.set("creditDebitBoth", "B");//XXX implement payment direction search
		//XXX implement: partner account is optional, it is not set
		request.setDouble("minAmount", 0.0D);//XXX implement amount range search
		request.setDouble("maxAmount", 0.0D);

		//FIXME if to-date is final the current date, additional dates final should be added final to the request final as the CIB final using forward final dated booking

		final SimpleNode reply = ( (BHASession) session ).sendRequest("AccHistory", "getAccountHistory", request);

		/* account history response
		items[0].transactionCode.text = 'K�RTYA K�SZP�NZFELV�TEL'
		items[0].transactionCode.code = '176'
		items[0].comment = 'xxxxxxxxxxxxxxxxxx'
		items[0].date = '20080225000000'
		items[0].amount = 'xxx0.00'
		....
		response.customerName = 'x y'
		response.accountName = 'xxxx-xxxxxx-xxx HUF Foly�'
		response.date = '20080324143753'
		 */
		//FIXME only the last six month can inquired, otherwise the backend system replaces the date range to fit the last half a year
		//by the user manual only 3 month can be inquired at once, however the back is able to return the full half a year in one request
		final SimpleNode[] itemNodes = reply.getNodeArray("items");

		if ( itemNodes == null )
		{
			historyItems = new HistoryItem[0];
		}
		else
		{
			historyItems = new HistoryItem[itemNodes.length];
			for ( int i = 0; i < itemNodes.length; i++ )
			{
				final SimpleNode node = itemNodes[i];
				final HistoryItem item = new HistoryItem();

				item.setOwner(account);
				item.setAmount(new Amount(node.getDouble("amount"), account.getCurrency()));
				try
				{
					item.setDate(node.getDate("date"));
				}
				catch ( final ParseException ex )
				{
					throw new ServiceException("Invalid reply date format: " + node.getString("date"), ex);
				}
				item.setDescription(node.getString("comment"));
				item.setBalance(new Amount(0, account.getCurrency()));
				item.setRemoteProperty("transactionCode.text", node.getStringPath("transactionCode.text"));
				item.setRemoteProperty("transactionCode.code", node.getStringPath("transactionCode.code"));

				historyItems[i] = item;
			}
		}
	}

	@SuppressWarnings( "deprecation" )
	private void depositHistory( final Session session, final BHADepositAccount account ) throws ServiceException
	{
		/*        super.aQ.k.set("reference", V[k1].getString("reference"));
		        super.aQ.k.set("branch", V[k1].getString("branch"));
		        super.aQ.k.set("type", V[k1].getString("type"));
		*/

		final SimpleNode request = new SimpleNode();

		request.set("branch", account.getBranch());
		request.set("reference", account.getReference());
		request.set("type", account.getDepositType());

		final SimpleNode reply = ( (BHASession) session ).sendRequest("AccOverview", "getDepositDetails", request);
		//SimpleNodeHelper.dumpNode("deposit.history", reply);

		/*.depositItems[0].date = '20080611000000'
		* .depositItems[0].narrative.text = 'Betét lekötés'
		.depositItems[0].narrative.code = 'PC'
		.depositItems[1].narrative.code = 'I' 'Kamatfizetés'
		.depositItems[2].narrative.code = 'I1''Kamatadó levonás'
		.depositItems[5].narrative.code = 'PM''Betét lejárat'
		*.depositItems[0].interest = '5.0000000'
		*.depositItems[0].amount = '50000.00'
		*.depositItems[0].currentAmount = '50000.00'
		*/

		final SimpleNode[] itemNodes = reply.getNodeArray("depositItems");

		if ( itemNodes == null )
		{
			historyItems = new HistoryItem[0];
		}
		else
		{
			final List<HistoryItem> matchingItems = new ArrayList<HistoryItem>();

			//filter manually
			for ( int i = 0; i < itemNodes.length; i++ )
			{
				final SimpleNode node = itemNodes[i];
				final HistoryItem item = new HistoryItem();

				item.setOwner(account);
				item.setRemoteProperty("interest", node.getDouble("interest"));

				try
				{
					Date date = node.getDate("date");
					if ( date == null )
					{
						date = new Date(2999, 0, 1);//if date is unknown, then a future date is set (this is missing in case of roll-over deposits
					}
					item.setDate(date);
				}
				catch ( final ParseException ex )
				{
					throw new ServiceException("Invalid reply date format: " + node.getString("date"), ex);
				}
				item.setDescription(node.getStringPath("narrative.text"));

				final String narrativeCode = node.getStringPath("narrative.code");
				item.setRemoteProperty("narrative.code", narrativeCode);

				double amountSign = 1;
				for ( int j = 0; j < DEPOSIT_CODES.length; j++ )
				{
					if ( DEPOSIT_CODES[j].equals(narrativeCode) )
						amountSign = DEPOSIT_CODE_SINGS[j];
				}

				item.setAmount(new Amount(amountSign * node.getDouble("amount"), account.getCurrency()));
				item.setBalance(new Amount(node.getDouble("currentAmount"), account.getCurrency()));

				//logger.finer("Deposit history item: " + item);

				if ( !from.after(item.getDate()) && !to.before(item.getDate()) )
				{
					matchingItems.add(item);
				}
			}

			historyItems = matchingItems.toArray(new HistoryItem[matchingItems.size()]);
			logger.fine("Number of matching deposit history items:" + historyItems.length);
		}
	}

	private void loanHistory( final Session session, final BHALoanAccount account )
	{
		logger.severe("Deposit account history is not yet implemented. Account: " + account);
		//TODO implement it: loanHistory
	}

	@Override
	public void execute( final Session session ) throws ServiceException
	{
		if ( !( session instanceof BHASession ) )
			throw new ServiceException("Incompatible session " + BHABank.class + " != " + session.getClass().getName());

		if ( account == null || from == null || to == null )
			throw new ServiceException("Service is not initialized yet correctly.");

		if ( account instanceof BHACurrentAccount )
			currentHistory(session, (BHACurrentAccount) account);
		else if ( account instanceof BHADepositAccount )
			depositHistory(session, (BHADepositAccount) account);
		else if ( account instanceof BHALoanAccount )
			loanHistory(session, (BHALoanAccount) account);
		else
			throw new IllegalArgumentException("Unsupport account in account history: " + account.getClass().getName());
	}
}
